
/**
 * AppleIIGo
 * File Disk Image class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include <string.h>
#include "fileutilities.h"
#include "FileDiskImage.h"
#include "GCR62DiskImage.h"
#include "GCRDisk35DiskImage.h"
#include "TwoImgDiskImage.h"

FileDiskImage * FileDiskImage::getInstance(UINT32 diskType,
										   char * diskImagePath) {
	if (!diskImagePath)
		return new FileDiskImage(diskType, 0, 0, 0, false);
	
	char * extension = getFileExtension(diskImagePath);
	UINT32 offset = 0;
	UINT32 byteNum = getFileByteNum(diskImagePath);
	
	bool isWriteProtectionForced = false;
	UINT8 gcrVolume = 254;
	bool isEncapsulated;
	
	do {
		isEncapsulated = false;
		
		if (!strcasecmp(extension, "po") ||
			!strcasecmp(extension, "vdsk") ||
			!strcasecmp(extension, "hdv")) {
			if (FileDiskImage::isDiskImageValid(diskType,
												diskImagePath,
												byteNum))
				return new FileDiskImage(diskType,
										 diskImagePath,
										 offset,
										 byteNum,
										 isWriteProtectionForced);
		} else if (!strcasecmp(extension, "2img") ||
				   !strcasecmp(extension, "2mg")) {
			if (!TwoImgDiskImage::isDiskImageValid(diskImagePath,
												   &offset,
												   &byteNum,
												   &extension,
												   &gcrVolume,
												   &isWriteProtectionForced))
				return false;
			
			isEncapsulated = true;
		} else
			return false;
	} while (isEncapsulated);
	
	return 0;
}

bool FileDiskImage::isDiskImageValid(UINT32 diskType,
									 char * diskImagePath,
									 UINT32 diskImageByteNum) {
	switch (diskType) {
		case FILEDISKTYPE_3_5:
			if (diskImageByteNum != GCRDISK35_DISK_BYTENUM)
				return false;
			break;
		case FILEDISKTYPE_HDD:
			if (diskImageByteNum == GCRDISK35_DISK_BYTENUM)
				return false;
			for (UINT32 index = 35; index < 40; index++)
				if (diskImageByteNum == (GCR62_TRACK_BYTENUM * index))
					return false;
			break;
		default:
			return false;
	}
	
	return true;
}

FileDiskImage::FileDiskImage(UINT32 diskType, char * diskImagePath,
							 UINT32 diskImageOffset,
							 UINT32 diskImageByteNum,
							 bool isWriteProtected) {
	this->diskImagePath = 0;
	
	if (!diskImagePath)
		return;
	
	if (!(diskImageFile = fopen(diskImagePath, "r+b"))) {
		if (!(diskImageFile = fopen(diskImagePath, "rb")))
			return;
		
		isWriteProtected = true;
	}
	
	this->diskImagePath = diskImagePath ? strdup(diskImagePath) : 0;
	this->diskImageOffset = diskImageOffset;
	diskImageBlockNum = diskImageByteNum / PRODOS_BLOCKBYTENUM;
	isWriteProtectedValue = isWriteProtected;
}

FileDiskImage::~FileDiskImage() {
	if (diskImageFile)
		fclose(diskImageFile);
	if (diskImagePath)
		delete diskImagePath;
}

bool FileDiskImage::seekBlock(UINT32 blockIndex) {
	if (blockIndex >= diskImageBlockNum)
		return false;
	
	return (fseek(diskImageFile, 
				  diskImageOffset + blockIndex * PRODOS_BLOCKBYTENUM,
				  SEEK_SET) == 0);
}

bool FileDiskImage::writeBlock(UINT8 * data) {
	if (isWriteProtectedValue)
		return false;
	
	return (fwrite(data, PRODOS_BLOCKBYTENUM, 1, diskImageFile) == 1);
}

bool FileDiskImage::readBlock(UINT8 * data) {
	return (fread(data, PRODOS_BLOCKBYTENUM, 1, diskImageFile) == 1);
}

bool FileDiskImage::isOnline() {
	return (diskImagePath != 0);
}

bool FileDiskImage::isWriteProtected() {
	return isWriteProtectedValue;
}

UINT32 FileDiskImage::getBlockNum() {
	return diskImageBlockNum;
}

char * FileDiskImage::getDiskImagePath() {
	return diskImagePath;
}
